package com.wu.springboot_study.util;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.*;

/**
 * @author benjamin_5
 * @Description Jedis工具类
 * @date 2022/12/22
 */
@AllArgsConstructor
@Component
public class JedisUtil {
    private static final Logger logger = LoggerFactory.getLogger(JedisUtil.class);

    private final JedisPool jedisPool;

    /**
     * 关闭资源
     * @param jedis
     */
    private void close(Jedis jedis){
        if(jedis != null){
            jedis.close();
        }
    }

    private String getKey(String namespace, String key){
        if(StringUtils.isNotBlank(namespace)){
            key = String.format("%s:%s", namespace, key);
        }
        return key;
    }

    private byte[] getKeyBytes(String namespace, String key){
        return getKey(namespace, key).getBytes();
    }

    /**
     * 设置缓存
     * @param namespace 命名空间
     * @param key 键
     * @param value 值
     * @param expireSecond 过期时间，<=0为不过期，单位s
     * @param <T>
     */
    public <T extends Serializable> void set(String namespace, String key, T value, int expireSecond){
        Jedis jedis = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            jedis = jedisPool.getResource();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(value);
            oos.flush();
            if(expireSecond <= 0){
                jedis.set(getKeyBytes(namespace, key), bos.toByteArray());
            }else{
                jedis.setex(getKeyBytes(namespace, key), expireSecond,bos.toByteArray());
            }
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            logger.error("对象序列化失败: {}" , e.getMessage());
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            close(jedis);
        }
    }

    public <T extends Serializable> void set(String namespace, String key, T value){
        set(namespace,key,value,0);
    }

    public <T extends Serializable> void set(String key, T value){
        set(null,key,value,0);
    }

    /**
     * 获取缓存
     * @param namespace 命名空间
     * @param key 键
     * @param <T> 返回值类型
     * @return
     */
    public <T extends Serializable> T get(String namespace,String key){
        Jedis jedis = null;
        ByteArrayInputStream bis = null;
        try {
            jedis = jedisPool.getResource();
            byte[] values = jedis.get(getKeyBytes(namespace, key));
            if (values == null) {
                return null;
            }
            bis = new ByteArrayInputStream(values);
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (T) ois.readObject();
        }catch (ClassNotFoundException e){
            logger.error("对象类型映射失败：{}" , e.getMessage());
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } catch (IOException e) {
            logger.error("对象反序列化失败: {}" , e.getMessage());
        } finally {
            try {
                if(bis != null){
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            close(jedis);
        }
        return null;
    }

    public <T extends Serializable> T get(String key){
        return get(null, key);
    }

    /**
     * 设置过期时间
     * @param namespace 命名空间
     * @param key 键
     * @param expireSecond 过期时间 单位s
     */
    public void expire(String namespace, String key, int expireSecond){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.expire(getKeyBytes(namespace, key), expireSecond);
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
    }

    public void expire(String key, int expireSecond){
        expire(null, key, expireSecond);
    }

    /**
     * key值是否存在
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public boolean exists(String namespace, String key){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.exists(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return false;
    }

    public boolean exists(String key){
        return exists(null, key);
    }

    /**
     * 删除缓存
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public boolean remove(String namespace, String key){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(getKeyBytes(namespace, key));
            return true;
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return false;
    }

    public boolean remove(String key){
        return remove(null, key);
    }

    /**
     * 键值加1
     * @param namespace 命名空间
     * @param key 键
     * @return
     */
    public Long incr(String namespace, String key){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.incr(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return null;
    }

    public Long incr(String key){
        return incr(null, key);
    }

    /**
     * 键值减1
     * @param namespace
     * @param key
     * @return
     */
    public Long decr(String namespace, String key){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.decr(getKeyBytes(namespace, key));
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return null;
    }

    public Long decr(String key){
        return decr(null, key);
    }


    /**
     * 获取锁
     * @param lockName
     * @param lockTime
     * @return
     */
    public boolean tryLock(String lockName, int lockTime){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            Long setnx = jedis.setnx(lockName, "1");
            if(setnx == 1){
                jedis.expire(lockName,lockTime);
                return true;
            }
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return false;
    }

    /**
     * 释放锁
     * @param lockName
     * @return
     */
    public boolean releaseLock(String lockName){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            jedis.del(lockName);
            return true;
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return false;
    }

    public Long setnx(String lockName){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.setnx(lockName, "1");
        }catch (JedisConnectionException e){
            logger.error("jedis connection fail: {}" , e.getMessage());
        } finally {
            close(jedis);
        }
        return 999L;
    }
}
